﻿using System;

namespace com.foxmail.wyyuan1991.Assignment.Solver
{
    /// <summary>
    /// Performace Function 路段间无相互影响
    /// </summary>
    /// <param name="a"></param>
    /// <param name="load"></param>
    /// <returns></returns>
    public delegate double SGPF(IArc a, double load);
    /// <summary>
    /// Performace Function 路段间相互影响
    /// </summary>
    /// <param name="a"></param>
    /// <param name="ifd"></param>
    /// <param name="load"></param>
    /// <returns></returns>
    public delegate double AGPF(IArc a, IFlowPattern ifd, double load);
    /// <summary>
    /// 经典F-W求解器
    /// </summary>
    public class FWSolver : IAssignmentAlgorithm
    {
        #region 属性
        /// <summary>
        /// 收敛阈值
        /// </summary>
        public double kesi { get; set; }
        /// <summary>
        /// 一维搜索算法
        /// </summary>
        public ILineSearchSolver LS { get; set; }
        /// <summary>
        /// 最短路搜索算法
        /// </summary>
        public IRouteSearchAlgorithm SA { get; set; }
        /// <summary>
        /// 全有全无分配算法
        /// </summary>
        public AoNSolver AS { get; set; }
        #endregion

        #region 方法
        /// <summary>
        /// 经典F-W算法
        /// </summary>
        /// <param name="net">网络</param>
        /// <param name="f">OD客流分布</param>
        /// <returns></returns>
        public IFlowPattern Solve(INetwork net, IDemand f)
        {
            return Solve(net, f,
                delegate (IArc a, double load)
                 {
                     return a.PerformaceFunction(load);
                 });
        }
        /// <summary>
        /// 经典F-W算法 自定义阻抗函数
        /// </summary>
        /// <param name="net">网络</param>
        /// <param name="f">OD客流分布</param>
        /// <param name="gpf">阻抗函数</param>
        /// <returns></returns>
        public IFlowPattern Solve(INetwork net, IDemand f, SGPF gpf)
        {
            CostMatrix c = new CostMatrix();
            FlowPattern fd3 = new FlowPattern();

            //①全有全无分配
            foreach (IArc a in net.ArcList)
            {
                c[a] = gpf(a, 0);
            }
            FlowPattern fd = AS.Solve(net, f, c) as FlowPattern;

            return Solve(net, f, gpf, fd);
        }
        /// <summary>
        /// 经典F-W算法 自定义阻抗函数 给定初始可行解
        /// </summary>
        /// <param name="net">网络</param>
        /// <param name="f">OD客流分布</param>
        /// <param name="gpf">阻抗函数</param>
        /// <param name="fd_i">初始可行解</param>
        /// <returns></returns>
        public IFlowPattern Solve(INetwork net, IDemand f, SGPF gpf, IFlowPattern fd_i)
        {
            CostMatrix c = new CostMatrix();
            FlowPattern fd3 = new FlowPattern();
            //①给定初始点
            FlowPattern fd = fd_i as FlowPattern;
            int iter = 1;
            while (Con(net, fd3, fd) > kesi && iter < 99)
            {
                if (iter > 1) { fd = fd3; fd3 = new FlowPattern(); }
                iter++;
                //②更新路阻函数
                c.Clear();
                foreach (IArc a in net.ArcList)
                {
                    c[a] = gpf(a, fd.getFlow(a));
                }

                //③搜索下降方向
                FlowPattern fd2 = AS.Solve(net, f, c) as FlowPattern;

                //④搜索步长
                double x = LS.Search(delegate (double alpha)
                {
                    double k = 0;
                    foreach (IArc a in net.ArcList)
                    {
                        double l = (1 - alpha) * fd.getFlow(a) + alpha * fd2.getFlow(a);
                        k += (fd2.getFlow(a) - fd.getFlow(a)) * gpf(a, l);
                    }
                    return k;
                }, 0, 1);
                foreach (IArc a in net.ArcList)
                {
                    fd3.setFlow(a, (1 - x) * (fd.getFlow(a)) + x * (fd2.getFlow(a)));
                }
            }
            return fd;
        }
        /// <summary>
        /// 对角化方法
        /// </summary>
        /// <param name="net"></param>
        /// <param name="f"></param>
        /// <param name="gpf"></param>
        /// <param name="fd_i"></param>
        /// <returns></returns>
        public IFlowPattern Solve(INetwork net, IDemand f, AGPF gpf, IFlowPattern fd_i)
        {
            CostMatrix c = new CostMatrix();
            FlowPattern fd2 = new FlowPattern();
            //①给定初始点
            FlowPattern fd = fd_i as FlowPattern;
            int iter = 0;
            while (DistenceBetweenFA(net, fd, fd2) > 0.01 && iter++ < 99)//Con(net, fd2, fd) > kesi && iter < 99)
            {
                if (iter > 1) { fd = fd2; fd2 = new FlowPattern(); }
                fd2 = this.Solve(net, f, delegate (IArc a, double load)
                   {
                       return gpf(a, fd, load);
                   }, fd) as FlowPattern;
            }
            return fd;
        }
        private double DistenceBetweenFA(INetwork net, IFlowPattern ifa1, IFlowPattern ifa2)
        {
            double sum = 0;
            foreach (IArc a in net.ArcList)
            {
                sum += Math.Abs(ifa1.getFlow(a) - ifa2.getFlow(a));
            }
            return sum;
        }
        //收敛计算
        private double Con(INetwork net, FlowPattern lateFD, FlowPattern prevFD)
        {
            double sum_1 = 0;
            double sum_2 = 0;
            foreach (IArc a in net.ArcList)
            {
                sum_1 += (lateFD.getFlow(a) - prevFD.getFlow(a)) * (lateFD.getFlow(a) - prevFD.getFlow(a));
                sum_2 += prevFD.getFlow(a);
            }
            if (sum_2 == 0) return double.MaxValue;
            return (double)Math.Sqrt(Convert.ToDouble(sum_1 * sum_1 / sum_2));
        }
        #endregion 
    }
}
